package cn.bugDance.domain.strategy.service.armory;

import cn.bugDance.domain.strategy.model.entity.RuleConfigEntity;
import cn.bugDance.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugDance.domain.strategy.model.entity.StrategyEntity;
import cn.bugDance.domain.strategy.model.valobj.StrategyConstant;
import cn.bugDance.domain.strategy.repository.IStrategyRepository;
import cn.bugDance.domain.strategy.service.algorithm.IAlgorithm;
import cn.bugDance.types.common.Constants;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RaffleStrategyArmory implements IRaffleStrategyArmory{

    @Resource
    private IStrategyRepository strategyRepository;

    @Resource
    private ApplicationContext applicationContext;

    @Value("${app.strategy.algorithm}")
    private String algorithmName;

    /**
     * 1. 根据strategyId查询strategy
     * @param strategyId
     */
    @Override
    public void assembleRaffleStrategy(Long strategyId, Date endTime) {
        IAlgorithm algorithm = applicationContext.getBean(algorithmName, IAlgorithm.class);
        algorithm.algorithmAssemble(strategyId, endTime);
    }

    @Override
    public Integer queryAwardRuleLockValue(Long strategyId, Integer awardId) {
        RuleConfigEntity ruleConfigEntity = strategyRepository.queryRuleConfigRuleAward(strategyId, awardId, StrategyConstant.StrategyRule.RULE_LOCK);
        return Integer.valueOf(ruleConfigEntity.getConfig());
    }

    private void cacheRaffleRate(List<StrategyAwardEntity> strategyAwardEntities, StrategyEntity strategyEntity, Date endTime){
        if(strategyEntity.isExistRuleModel(StrategyConstant.StrategyRule.RULE_WEIGHT)){
            RuleConfigEntity ruleConfigEntity = strategyRepository.queryRuleConfigRule(strategyEntity.getStrategyId(), StrategyConstant.StrategyRule.RULE_WEIGHT);
            Map<Integer, List<Integer>> levelMap = ruleConfigEntity.ruleConfigSplit();
            if(levelMap.isEmpty()) return;

            Set<Integer> levelSet = levelMap.keySet();
            for(Integer level : levelSet){
                List<Integer> awardIds = levelMap.get(level);
                List<StrategyAwardEntity> strategyAwardEntitiesCopy = strategyAwardEntities;
                strategyAwardEntitiesCopy = strategyAwardEntitiesCopy.stream().filter(strategyAwardEntity -> awardIds.contains(strategyAwardEntity.getAwardId())).collect(Collectors.toList());
                String cacheRateTableKey = Constants.Redis.STRATEGY_RATE_TABLE + strategyEntity.getStrategyId() + ":" + level;
                String cacheRateLevelKey = Constants.Redis.STRATEGY_RATE_LEVEL + strategyEntity.getStrategyId() + ":" + level;
                cacheRaffleRate(strategyAwardEntitiesCopy, cacheRateTableKey, cacheRateLevelKey);
            }
        }
        String cacheRateTableKey = Constants.Redis.STRATEGY_RATE_TABLE + strategyEntity.getStrategyId() + ":" + Constants.Redis.STRATEGY_RATE_NO_LEVEL;
        String cacheRateLevelKey = Constants.Redis.STRATEGY_RATE_LEVEL + strategyEntity.getStrategyId() + ":" + Constants.Redis.STRATEGY_RATE_NO_LEVEL;
        List<StrategyAwardEntity> strategyAwardEntitiesCopy = strategyAwardEntities.stream().filter(strategyAwardEntity -> strategyAwardEntity.getRaffle() == 0).collect(Collectors.toList());
        cacheRaffleRate(strategyAwardEntitiesCopy, cacheRateTableKey, cacheRateLevelKey);

        cacheRaffleAwardCount(strategyAwardEntitiesCopy, endTime);
    }

    private void cacheRaffleAwardCount(List<StrategyAwardEntity> strategyAwardEntitiesCopy, Date endTime) {
        for(StrategyAwardEntity strategyAwardEntity : strategyAwardEntitiesCopy){
            String cacheAwardCountKey = Constants.Redis.STRATEGY_AWARD_COUNT + strategyAwardEntity.getStrategyId() + ":" + strategyAwardEntity.getAwardId();
            strategyRepository.storeAwardCount(cacheAwardCountKey, strategyAwardEntity.getAwardCount(), strategyAwardEntity.getStrategyId(), strategyAwardEntity.getAwardId(), endTime);
        }
    }

    /**
     * 1. 找出最小概率
     * 2. 计算出概率范围
     * 3. 组装奖品表
     * 4. 存入Redis
     * @param strategyAwardEntities
     * @param cacheRateTableKey
     * @param cacheRateLevelKey
     */
    private void cacheRaffleRate(List<StrategyAwardEntity> strategyAwardEntities, String cacheRateTableKey, String cacheRateLevelKey){

        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);

        BigDecimal scale = BigDecimal.valueOf(convert(minAwardRate.doubleValue()));

        List<Integer> rateTable = new ArrayList<>();
        for(StrategyAwardEntity strategyAwardEntity : strategyAwardEntities){
            Integer awardId = strategyAwardEntity.getAwardId();
            BigDecimal awardRate = strategyAwardEntity.getAwardRate();

            int rateRange = awardRate.multiply(scale).setScale(0, RoundingMode.CEILING).intValue();
            for(int i = 0; i < rateRange; i++){
                rateTable.add(awardId);
            }
        }
        long rateRangeSum = rateTable.size();
        Collections.shuffle(rateTable);
        strategyRepository.storeToCache(rateTable, rateRangeSum, cacheRateTableKey, cacheRateLevelKey);

    }

    private double convert(double minAwardRate){
        double rateRange = 1;
        while(minAwardRate < 1){
            minAwardRate *= 10;
            rateRange *= 10;
        }
        return rateRange;
    }
}
